package org.sction.util;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.zip.CRC32;
import java.util.zip.CheckedOutputStream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.util.zip.ZipOutputStream;

import org.apache.log4j.Logger;

public class ZipUtils {
	static Logger log = Logger.getLogger(ZipUtils.class);

	public static void print(ZipEntry e) {
		PrintStream err = System.err;
		log.info("添加文件  " + e.getName());
		if (e.getMethod() == ZipEntry.DEFLATED) {
			long size = e.getSize();
			if (size > 0) {
				long csize = e.getCompressedSize();
				long ratio = ((size - csize) * 100) / size;
				err.println("   (压缩率   " + ratio + "%) ");
			} else {
				err.println("   (压缩率   0%) ");
			}
		} else {
			err.println("   (存储   0%) ");
		}
	}

	public static void unZip(String path, String destpath) {
		int count = -1;
		File temp = new File(destpath);
		if (!temp.exists()) {
			temp.mkdirs();
		}
		File file = null;
		InputStream is = null;
		FileOutputStream fos = null;
		BufferedOutputStream bos = null;
		try {
			ZipFile zipFile = new ZipFile(path);

			Enumeration<?> entries = zipFile.entries();

			while (entries.hasMoreElements()) {
				byte buf[] = new byte[BUFFER];

				ZipEntry entry = (ZipEntry) entries.nextElement();

				String filename = entry.getName();

				if (filename == null || filename.equals("")) {
					continue;
				}
				filename = destpath + "/" + filename;

				file = new File(filename);
				if (entry.isDirectory() && !file.exists()) {
					file.mkdirs();
					continue;// 解压目录
				} else {
					file.createNewFile();
				}

				System.out.println(file.getPath());
				is = zipFile.getInputStream(entry);

				fos = new FileOutputStream(file);
				bos = new BufferedOutputStream(fos, BUFFER);

				while ((count = is.read(buf)) > -1) {
					bos.write(buf, 0, count);
				}

				fos.close();

				is.close();
			}

			zipFile.close();

		} catch (IOException ioe) {
			ioe.printStackTrace();
		}
	}

	public static void zip(String outPath, List<Map<String, Object>> listFile) {
		zip(outPath, listFile, "GBK");
	}

	// 此程序也可以改成压缩某几个文件，程序略加修改，传入要压缩的几个文件路径就可以了。如下：
	public static void zip(String outPath, List<Map<String, Object>> listFile,
			String encoding) {
		try {
			byte bs[] = new byte[512];
			File ft = new File(outPath).getParentFile();
			if (!(ft.exists() && ft.isDirectory())) {
				ft.mkdirs();
			}
			ZipOutputStream out;
			File outf = new File(outPath);
			CheckedOutputStream fos;
			fos = new CheckedOutputStream(new FileOutputStream(outf),
					new CRC32());
			out = new ZipOutputStream(fos);
			if (LEVEL > -1 && LEVEL < 10) {
				out.setLevel(LEVEL);
			}
			for (int i = 0; i < listFile.size(); i++) {
				Map<String, Object> map = listFile.get(i);
				Object of = map.get("file");
				File filename = null;
				if (of instanceof java.io.File) {
					filename = (File) of;
				} else {
					filename = new File(of.toString());
				}
				if (!filename.exists()) {
					filename = new File(FileUtils.FILE_UPLOAD_DIR
							+ filename.getPath());
				}
				if (filename.exists()) {
					ZipEntry e = new ZipEntry(map.get("name").toString());
					out.putNextEntry(e);
					BufferedInputStream in = new BufferedInputStream(
							new FileInputStream(filename));
					// FileInputStream in = new FileInputStream(filename);
					int len = 0;
					while ((len = in.read(bs)) != -1) {
						out.write(bs, 0, len);
					}

					in.close();
				}
				out.closeEntry();
				// this.print(e);
			}
			out.close();
		} catch (FileNotFoundException e1) {
			log.error("文件没有发现", e1);
		} catch (IOException e) {
			log.error("文件输出输入错误", e);
		}
	}

	public static void zip(String outPath, String srcDir, String encoding) {
		try {
			byte bs[] = new byte[512];
			File ft = new File(outPath).getParentFile();
			if (!(ft.exists() && ft.isDirectory())) {
				ft.mkdirs();
			}
			ZipOutputStream out;
			File outf = new File(outPath);
			CheckedOutputStream fos;
			fos = new CheckedOutputStream(new FileOutputStream(outf),
					new CRC32());
			out = new ZipOutputStream(fos);
			if (LEVEL > -1 && LEVEL < 10) {
				out.setLevel(LEVEL);
			}
			File dir = new File(srcDir);
			File[] files = dir.listFiles();
			for (int i = 0; i < files.length; i++) {
				File file = files[i];

				if (file.exists()) {
					ZipEntry e = new ZipEntry(file.getName().toString());
					out.putNextEntry(e);
					if (file.isDirectory()) {
						continue;
					}
					BufferedInputStream in = new BufferedInputStream(
							new FileInputStream(file));
					// FileInputStream in = new FileInputStream(filename);
					int len = 0;
					while ((len = in.read(bs)) != -1) {
						out.write(bs, 0, len);
					}

					in.close();
				}
				out.closeEntry();
				// this.print(e);
			}
			out.close();
		} catch (FileNotFoundException e1) {
			log.error("文件没有发现", e1);
		} catch (IOException e) {
			log.error("文件输出输入错误", e);
		}
	}

	public static void main(String[] temp) {
		String zipFileName = "F:\\tem\\test.zip"; // 压缩后的zip文件
		List<Map<String, Object>> listFile = new ArrayList<Map<String, Object>>();
		Map<String, Object> map = new HashMap<String, Object>();
		map.put("file", "F:/OA文档/师范大学/天津师范大学办公系统用户使用说明书 - 简版.doc");
		map.put("name", "天津师范大学办公系统用户使用说明书 - 简版.doc");
		listFile.add(map);
		Map<String, Object> map2 = new HashMap<String, Object>();
		map2.put("file", "F:/OA文档/师范大学/天津师范大学办公系统用户使用说明书 - 简版.pdf");
		map2.put("name", "天津师范大学办公系统用户使用说明书 - 简版.pdf");
		listFile.add(map2);
		Map<String, Object> map3 = new HashMap<String, Object>();
		map3.put("file", "F:/OA文档/师范大学/天津师范大学办公系统用户使用说明书 - 简版.pdf");
		map3.put("name", "天津师范大学办公系统用户使用说明书 - 简版3.pdf");
		listFile.add(map3);
		Map<String, Object> map4 = new HashMap<String, Object>();
		map4.put("file", "F:/OA文档/师范大学/天津师范大学办公系统用户使用说明书 - 简版.pdf");
		map4.put("name", "天津师范大学办公系统用户使用说明书 - 简版4.pdf");
		listFile.add(map4);
		Map<String, Object> map5 = new HashMap<String, Object>();
		map5.put("file", "F:/OA文档/师范大学/天津师范大学办公系统用户使用说明书 - 简版.pdf");
		map5.put("name", "天津师范大学办公系统用户使用说明书 - 简版5.pdf");
		listFile.add(map5);
		long start = System.currentTimeMillis();
		try {
			ZipUtils.zip(zipFileName, listFile);

			// GZip.compress("F:/OA文档/师范大学/", "C:\\tk\\a.zip");
		} catch (Exception ex) {
			ex.printStackTrace();
		}
		log.info((System.currentTimeMillis() - start) + "毫秒");
	}

	public static final String EXT = ".zip";
	private static final String BASE_DIR = "";
	private static final String PATH = "/";
	private static final int BUFFER = 1024;
	// private static String ENCODING = "";
	private static int LEVEL = -1;

	/**
	 * 文件压缩
	 * 
	 * @param srcPath
	 *            源文件路径
	 * @param destPath
	 *            目标文件路径
	 * 
	 */
	public static void compress(String srcPath, String destPath)
			throws Exception {
		File srcFile = new File(srcPath);
		compress(srcFile, destPath);
	}

	/**
	 * 压缩
	 * 
	 * @param srcPath
	 * @throws Exception
	 */
	public static void compress(String srcPath) throws Exception {
		File srcFile = new File(srcPath);
		compress(srcFile);
	}

	/**
	 * 压缩
	 * 
	 * @param srcFile
	 * @throws Exception
	 */
	public static void compress(File srcFile) throws Exception {
		String name = srcFile.getName();
		String basePath = srcFile.getParent();
		String destPath = basePath + name + EXT;
		compress(srcFile, destPath);
	}

	/**
	 * 压缩
	 * 
	 * @param srcFile
	 *            源路径
	 * @param destPath
	 *            目标路径
	 * @throws Exception
	 */
	public static void compress(File srcFile, File destFile) throws Exception {
		if (!destFile.getParentFile().exists()
				|| !destFile.getParentFile().isDirectory()) {
			destFile.getParentFile().mkdirs();
		}
		// 对输出文件做CRC32校验
		CheckedOutputStream cos = new CheckedOutputStream(new FileOutputStream(
				destFile), new CRC32());
		ZipOutputStream zos = new ZipOutputStream(cos);
		if (LEVEL > -1 && LEVEL < 10) {
			zos.setLevel(LEVEL);
		}
		compress(srcFile, zos, BASE_DIR);
		zos.closeEntry();
		zos.flush();
		zos.close();
	}

	/**
	 * @param srcFile
	 * @param destPath
	 * @throws Exception
	 */
	public static void compress(File srcFile, String destPath) throws Exception {
		compress(srcFile, new File(destPath));
	}

	/**
	 * 压缩
	 * 
	 * @param srcFile
	 *            源路径
	 * @param zos
	 *            ZipOutputStream
	 * @param basePath
	 *            压缩包内相对路径
	 * @throws Exception
	 */
	private static void compress(File srcFile, ZipOutputStream zos,
			String basePath) throws Exception {
		if (srcFile.isDirectory()) {
			compressDir(srcFile, zos, basePath);
		} else {
			compressFile(srcFile, zos, basePath);
		}
	}

	/**
	 * 压缩目录
	 * 
	 * @param dir
	 * @param zos
	 * @param basePath
	 * @throws Exception
	 */
	private static void compressDir(File dir, ZipOutputStream zos,
			String basePath) throws Exception {
		for (File file : dir.listFiles()) {
			// 递归压缩
			compress(file, zos, basePath + dir.getName() + PATH);

		}
	}

	/**
	 * /** 文件压缩
	 * 
	 * @param file
	 *            待压缩文件
	 * @param zos
	 *            ZipOutputStream
	 * @param dir
	 *            压缩文件中的当前路径
	 * @throws Exception
	 */
	private static void compressFile(File file, ZipOutputStream zos, String dir)
			throws Exception {
		/**
		 * 压缩包内文件名定义
		 * 
		 * <pre>
		 * 如果有多级目录，那么这里就需要给出包含目录的文件名  
		 * 如果用WinRAR打开压缩包，中文名将显示为乱码
		 * </pre>
		 */
		ZipEntry entry = new ZipEntry(dir + file.getName());
		zos.putNextEntry(entry);
		BufferedInputStream bis = new BufferedInputStream(new FileInputStream(
				file));
		int count;
		byte[] bs = new byte[BUFFER];
		while ((count = bis.read(bs)) != -1) {
			zos.write(bs, 0, count);
		}
		bis.close();
	}
}
